﻿using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using ArlesInterfaces.Render;

namespace Render
{
    public class Camera: ICamera
    {

        protected Vector3 position;

        Matrix world;
        Matrix view;
        Matrix projection;
        
        Vector3 lookat;
        float aspectRatio;
        float nearPlane;
        float farPlane;
        float fov;

        MouseState currentState;
        MouseState previousState;
        KeyboardState keyState;

        bool _move;
        enum CameraState
        {
            Normal,
            First,
            Pan,
            Edit
        }
        bool cameraActive;
        CameraState status;

        Vector2 lastCursor;
        Vector3 angle;
        float turnSpeed;
        float speed;

        #region ICamera Members

        public Matrix World
        {
            get { return world; }
        }

        public Matrix View
        {
            get { return view; }
        }

        public Matrix Projection
        {
            get { return projection; }
        }

        public bool CameraActive
        {
            set {
                cameraActive = value;
                _move = false;
            }
            get { return cameraActive; }
        }

        public float AspectRatio
        {
            get { return aspectRatio; }
            set
            {
                aspectRatio = value;
                InitCamera();
            }
        }

        public Vector3 Lookat
        {
            get { return lookat; }
            set
            {
                lookat = value;
                InitCamera();
            }
        }

        public Vector3 Position
        {
            get { return position; }
            set { position = value; }
        }
        #endregion

        public Camera(Vector3 position)
        {
            this.position = position;
            this.lookat = Vector3.Zero;
            aspectRatio = 1f;
            nearPlane = 1f;
            farPlane = 100000f;
            turnSpeed = 5.0f;
            speed = 120.0f;
            cameraActive = false;
            fov = MathHelper.PiOver4;
            InitCamera();
        }

        public Camera(Vector3 position, Vector3 lookat)
        {
            this.position = position;
            this.lookat = lookat;
            aspectRatio = 1f;
            nearPlane = 1f;
            farPlane = 100000f;
            turnSpeed = 5.0f;
            speed = 120.0f;
            cameraActive = false;
            fov = MathHelper.PiOver4;
            InitCamera();
        }

        private void InitCamera()
        {
            this.view = Matrix.CreateLookAt(position, lookat, Vector3.Up);
            this.projection = Matrix.CreatePerspectiveFieldOfView(fov, aspectRatio, nearPlane, farPlane);
            this.world = Matrix.Identity;
            _move = false;
        }

        public void Update(GameTime gameTime)
        {
            ProcessInput(gameTime, cameraActive);
            UpdateViewMatrix();
        }

        private void ProcessInput(GameTime gameTime, bool mouseActive)
        {
            float step = (float)gameTime.ElapsedGameTime.TotalSeconds;
            currentState = Mouse.GetState();

            keyState = Keyboard.GetState();

            Vector3 forward;
            Vector3 left;

            forward = Vector3.Normalize(new Vector3((float)Math.Sin(-angle.Y) *
                    (float)Math.Cos(angle.X), (float)Math.Sin(angle.X),
                    (float)Math.Cos(-angle.Y) * (float)Math.Cos(angle.X)));
            left = Vector3.Normalize(new Vector3((float)Math.Cos(angle.Y),
                0f, (float)Math.Sin(angle.Y)));

            if (cameraActive)
            {
                if (currentState.RightButton == ButtonState.Pressed &&
                    previousState.RightButton != ButtonState.Pressed)
                {
                    status = CameraState.First;
                    lastCursor = new Vector2(currentState.X, currentState.Y);
                }
                if (currentState.RightButton != ButtonState.Pressed &&
                    previousState.RightButton == ButtonState.Pressed)
                    status = CameraState.Normal;
                if (currentState.MiddleButton == ButtonState.Pressed &&
                    previousState.MiddleButton != ButtonState.Pressed)
                    status = CameraState.Pan;
                if (currentState.MiddleButton != ButtonState.Pressed &&
                    previousState.MiddleButton == ButtonState.Pressed)
                    status = CameraState.Normal;

                Vector2 dMove = new Vector2(currentState.X - previousState.X,
                        currentState.Y - previousState.Y);

                int dWheel = currentState.ScrollWheelValue-previousState.ScrollWheelValue;
                if (dWheel != 0)
                    position -= forward * (float)dWheel * 4.0f * step;
               
                if (status == CameraState.First)
                {
                    this.angle.X += (currentState.Y - lastCursor.Y) * turnSpeed * 0.001f;
                    this.angle.Y += (currentState.X - lastCursor.X) * turnSpeed * 0.001f;
                    Mouse.SetPosition((int)lastCursor.X, (int)lastCursor.Y);                    
                }
                if (status == CameraState.Pan)
                {
                    position -= left * dMove.X * speed * step * 0.1f;
                    position += new Vector3(0, dMove.Y, 0) * speed * step * 0.1f;
                }

                if (keyState.IsKeyDown(Keys.W))
                    position -= forward * speed * step;
                if (keyState.IsKeyDown(Keys.S))
                    position += forward * speed * step;
                if (keyState.IsKeyDown(Keys.A))
                    position -= left * speed * step;
                if (keyState.IsKeyDown(Keys.D))
                    position += left * speed * step;
                if (keyState.IsKeyDown(Keys.Q))
                    position += Vector3.Down * speed * step;
                if (keyState.IsKeyDown(Keys.E))
                    position += Vector3.Up * speed * step;
            }

            previousState = currentState;
        }

        private void UpdateViewMatrix()
        {
            view = Matrix.Identity;
            view *= Matrix.CreateTranslation(-position);
            view *= Matrix.CreateRotationZ(angle.Z);
            view *= Matrix.CreateRotationY(angle.Y);
            view *= Matrix.CreateRotationX(angle.X);
        }

        private void UpdateProjectionMatrix()
        {
            projection = Matrix.CreatePerspectiveFieldOfView(fov, aspectRatio, nearPlane, farPlane);
        }
    }
}